#ifndef ROCKSDB_BASIC_OPERATOR_H_
#define ROCKSDB_BASIC_OPERATOR_H_

#include "vet_index_common.h"

#include "rocksdb/db.h"
#include "rocksdb/slice.h"
#include "rocksdb/options.h"
#include "rocksdb/iterator.h"
#include "auto_roll_logger.h"
#include "rocksdb/write_batch.h"

#include "singleton.h"
#include "noncopyable.h"

VET_INDEX_NAMESPACE_BEGIN

enum COLUMN_FAMILY_TYPE
{
    E_COL_FAM_VECTOR_DATA = 0,      // save faiss RAM entire vector data
    E_COL_FAM_META_DATA = 1,        // save metadata info
    E_COL_FAM_DEFAULT,              // now nouse, but must have it
    
    E_COL_FAM_TOTAL_NUM,
};

class RocksDbOperator : private noncopyable
{
public:
    RocksDbOperator();
    virtual ~RocksDbOperator();

public:
    static RocksDbOperator* Instance() {
        return CSingleton<RocksDbOperator>::Instance();
    };

    static void Destroy() {
        CSingleton<RocksDbOperator>::Destroy();
    };

public:

    bool InitRocksDb(const std::string& s_db_dir);

    /** insert key-value into specific database
     *  if key exist, return 'duplicate key' error
     * @param key           input specific key
     * @param value         input specific value
     * @param b_sync_mode   whether wait for data being written to persistent storage
     * @param col_fam_type  database that key-value write into
     * @return error id
    */
    int InsertEntry(
        const std::string& key,
        const std::string& value,
        bool b_sync_mode = false, 
        int col_fam_type = E_COL_FAM_VECTOR_DATA);

    /** delete key-value that in specific database
     * 
     * @param key           input delete key
     * @param b_sync_mode   whether wait for data being written to persistent storage
     * @param col_fam_type  database that key-value belong to
     * @return error id
    */
    int DeleteEntry(
        const std::string& key,
        bool b_sync_mode = true,
        int col_fam_type = E_COL_FAM_VECTOR_DATA);

    /** update key-value that in specific database
     *  if key not exist, do nothing, return 'key no found' error
     * @param key           input specific key
     * @param value         input specific value
     * @param b_sync_mode   whether wait for data being written to persistent storage
     * @param col_fam_type  database that key-value write into
     * @return error id
    */
    int UpdateEntry(
        const std::string& key,
        const std::string& value,
        bool b_sync_mode = false, 
        int col_fam_type = E_COL_FAM_VECTOR_DATA);

    using UpdatePair = std::vector<std::pair<std::string , std::string> >;
    /** update multi keys atomic batch that in specific database,
     * if delete key is not existed , just ignore it,
     * if update key is existed,just replace value
     * 
     * @param delete_vet    delete key lists
     * @param update_vet    k-v update pairs(default:empty)
     * @param b_sync_mode   whether wait for data being delete to persistent storage
     * @param col_fam_type  database that key-value belong to
     * @return error id
    */
    int BatchUpdate(
        const std::set<std::string>& delete_set,
        const UpdatePair& update_vet = UpdatePair(),
        bool b_sync_mode = false,
        int col_fam_type = E_COL_FAM_VECTOR_DATA);

    /** get value by key that full matched
     *  
     * @param key               input specific key 
     * @param value             output wanted value
     * @param col_fam_type      database that key exited
     * @return error id
    */
    int GetEntry(
        const std::string& key,
        std::string& value,
        int col_fam_type = E_COL_FAM_VECTOR_DATA);
    
    bool MultiGetEntry(
        const std::vector<int>& col_fam_type_vet,
        const std::vector<std::string>& keys,
        std::vector<std::string>& values,
        std::set<int>& error_seq_id,
        std::set<int>& no_found_seq_id);

    /** replace key-value that in specific database
     *  if key not exist, insert it , otherwise, update its value
     * @param key           input specific key
     * @param value         input specific value
     * @param b_sync_mode   whether wait for data being written to persistent storage
     * @param col_fam_type  database that key-value write into
     * @return error id
    */
    int ReplaceEntry(
        const std::string& key,
        const std::string& value,
        bool b_sync_mode = false, 
        int col_fam_type = E_COL_FAM_VECTOR_DATA);

    /** get specific database iterator
     * 
     * @param col_fam_type database column family type
     * @return storage data iterator 
    */
    rocksdb::Iterator* GetIterator(int col_fam_type = E_COL_FAM_VECTOR_DATA);

private:
    int key_exist_check(
        const std::string& key,
        int col_fam_type);

    int setup_db_options(rocksdb::DBOptions& option);
    int setup_col_fam_options(rocksdb::ColumnFamilyOptions& option);
    int setup_block_based_table_options(rocksdb::ColumnFamilyOptions& option);
    int setup_plain_table_options(rocksdb::DBOptions& option) { return 0;};

    rocksdb::Status create_column_family(const rocksdb::Options& options);
    int create_dir(const std::string& s_path); // only support single dir create

private:
    rocksdb::DB* p_rocksdb_db_;
    rocksdb::Env* p_rocksdb_env_;

    std::vector<rocksdb::ColumnFamilyHandle*> col_fam_handle_vet_;
    std::shared_ptr<rocksdb::Logger> logger_;
    std::string s_current_dir_;
};

VET_INDEX_NAMESPACE_END

#endif